package com.ideaaedi.zoo.diy.artifact.mybatisplus.interceptor;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.handler.FullSqlPreparePreHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.util.CollectionUtils;

import javax.annotation.Nonnull;
import java.sql.Connection;
import java.text.DateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;

/**
 * (non-javadoc)
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
public class SqlPreparePreInterceptor implements InnerInterceptor {
    
    private final List<FullSqlPreparePreHandler> fullSqlPreparePreHandlers;
    
    public SqlPreparePreInterceptor(List<FullSqlPreparePreHandler> fullSqlPreparePreHandlers) {
        this.fullSqlPreparePreHandlers = fullSqlPreparePreHandlers;
    }
    
    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        if (CollectionUtils.isEmpty(fullSqlPreparePreHandlers)) {
            return;
        }
        PluginUtils.MPStatementHandler mpStatementHandler = PluginUtils.mpStatementHandler(sh);
        List<FullSqlPreparePreHandler> fullSqlPreparePreHandlersList = fullSqlPreparePreHandlers.stream()
                .filter(fullSqlPreparePreHandler -> fullSqlPreparePreHandler.support(mpStatementHandler))
                .toList();
        if (CollectionUtils.isEmpty(fullSqlPreparePreHandlersList)) {
            return;
        }
        Configuration configuration = mpStatementHandler.configuration();
        BoundSql boundSql = mpStatementHandler.boundSql();
        String fullSql = assembleSql(configuration, boundSql);
        for (FullSqlPreparePreHandler fullSqlPreparePreHandler : fullSqlPreparePreHandlersList) {
            fullSqlPreparePreHandler.handle(fullSql, boundSql, configuration);
        }
    }
    
    /**
     * 组装获取完整的SQL
     */
    private String assembleSql(Configuration configuration, BoundSql boundSql) {
        String sqlWithPlaceHolder = boundSql.getSql().replaceAll("[\\s]+", " ");
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings == null || parameterMappings.size() == 0 || parameterObject == null) {
            return sqlWithPlaceHolder;
        }
        // 没有任何占位符
        int firstPlaceholderIdx = sqlWithPlaceHolder.indexOf("?");
        if (firstPlaceholderIdx < 0) {
            return sqlWithPlaceHolder;
        }
        // 有类型解析器
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            return sqlWithPlaceHolder.replaceFirst("\\?", Matcher.quoteReplacement(determineParameterValue(parameterObject)));
        }
        // 无类型解析器
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append(sqlWithPlaceHolder, 0, firstPlaceholderIdx);
        sqlWithPlaceHolder = sqlWithPlaceHolder.substring(firstPlaceholderIdx);
        for (ParameterMapping parameterMapping : parameterMappings) {
            String propertyName = parameterMapping.getProperty();
            String parameterValue;
            if (metaObject.hasGetter(propertyName)) {
                Object obj = metaObject.getValue(propertyName);
                parameterValue = determineParameterValue(obj);
            } else if (boundSql.hasAdditionalParameter(propertyName)) {
                Object obj = boundSql.getAdditionalParameter(propertyName);
                parameterValue = determineParameterValue(obj);
            } else {
                continue;
            }
            sqlBuilder.append(parameterValue);
            sqlWithPlaceHolder = sqlWithPlaceHolder.substring(1);
            firstPlaceholderIdx = sqlWithPlaceHolder.indexOf("?");
            if (firstPlaceholderIdx < 0) {
                sqlBuilder.append(sqlWithPlaceHolder);
                break;
            }
            sqlBuilder.append(sqlWithPlaceHolder, 0, firstPlaceholderIdx);
            sqlWithPlaceHolder = sqlWithPlaceHolder.substring(firstPlaceholderIdx);
        }
        return sqlBuilder.toString();
    }
    
    /**
     * 获取参数值
     */
    @Nonnull
    private String determineParameterValue(Object obj) {
        if (obj instanceof String) {
            return "'" + obj + "'";
        }
        if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT,
                    Locale.getDefault());
            return "'" + formatter.format(obj) + "'";
        }
        if (obj instanceof LocalDate) {
            return "'" + ((LocalDate) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "'";
        }
        if (obj instanceof LocalTime) {
            return "'" + ((LocalTime) obj).format(DateTimeFormatter.ofPattern("HH:mm:ss")) + "'";
        }
        if (obj instanceof LocalDateTime) {
            return "'" + ((LocalDateTime) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "'";
        }
        if (obj != null) {
            return obj.toString();
        }
        return "";
    }
}
